from PyQt4.QtCore import *
from PyQt4.QtCore import QAbstractTableModel
from PyQt4.QtCore import QModelIndex
from PyQt4.QtCore import QString
from PyQt4.QtCore import QVariant
from PyQt4.QtCore import Qt
from PyQt4.QtCore import SIGNAL, QSize
from PyQt4.QtGui import QAbstractItemView
from PyQt4.QtGui import QAction
from PyQt4.QtGui import QCursor
from PyQt4.QtGui import QFont
from PyQt4.QtGui import QHeaderView
from PyQt4.QtGui import QMenu
from PyQt4.QtGui import QTableView
import os
from phonik.informer import Informer

from phonik.utils import formatTime

class Playlist(QTableView):
    def __init__(self, informer):
        QTableView.__init__(self)

        self.informer = informer

        # model
        self.model = PlayListModel(self)
        self.setModel(self.model)

        # playlist
        self.setShowGrid(False)
        self.setSortingEnabled(True)
        self.setEditTriggers(QAbstractItemView.AllEditTriggers)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.setFont(QFont("Sans", 9))
        self.setFocusPolicy(Qt.NoFocus)

        # vertical header
        self.verticalHeader().hide()
        self.verticalHeader().setDefaultSectionSize(21)

        # horizontal header
        self.horizontalHeader().setSortIndicatorShown(True)
        self.horizontalHeader().setResizeMode(1, QHeaderView.Stretch)
        self.horizontalHeader().setFont(QFont("Sans", 7))
        self.horizontalHeader().setFixedHeight(17)
        self.horizontalHeader().setContextMenuPolicy(Qt.CustomContextMenu)
        self.horizontalHeader().customContextMenuRequested.connect(self.showHeaderContextMenu)
        self.horizontalHeader().setMovable(True)

        #drag and drop
        self.setDragDropMode(QAbstractItemView.DragDrop)
        self.setDragEnabled(True)
        self.setAcceptDrops(True)


    def showHeaderContextMenu(self):
        menu = QMenu()

        for i in range(0, self.model.columnCount()):
            action = menu.addAction(self.model.headerData(i, Qt.Horizontal).toString())
            action.setProperty('column', QVariant(i))
            action.setCheckable(True)
            if not self.isColumnHidden(i):
                action.setChecked(True)

        self.connect(menu, SIGNAL('triggered (QAction *)'), self.runHeaderContextMenu)
        menu.exec_(QCursor.pos())


    def runHeaderContextMenu(self, action):
        column = int(action.property('column').toString())
        self.setColumnHidden(column, not self.isColumnHidden(column))


    def currentRow(self):
        return self.currentIndex().row()


    def addSong(self, url, info, tags, autosort=False):
        keys = tags.keys()

        song = []

        # tracknumber
        if 'tracknumber' in keys:
            tn = tags['tracknumber'][0]

            # add additional zeros to have adequate sorting
            if '/' in tn:
                sp = tn.split('/')
                if len(sp[0]) == 1:
                    tn = '0' + tn
                sp = tn.split('/')
                if len(sp[1]) == 1:
                    tn = sp[0] + '/0' + sp[1]
            elif len(tn) == 1:
                tn = '0' + tn

            song.append(tn)
        else: song.append('')

        # song title
        if 'title' in keys: song.append(tags['title'][0])
        else: song.append(os.path.basename(url))

        # artist
        if 'artist' in keys: song.append(tags['artist'][0])
        else: song.append('')

        # album
        if 'album' in keys: song.append(tags['album'][0])
        else: song.append('')

        # date of album
        if 'date' in keys: song.append(tags['date'][0])
        else: song.append('')

        # song duration
        song.append(formatTime(info.length))

        # genre
        if 'genre' in keys: song.append(tags['genre'][0])
        else: song.append('')

        # entry path
        song.append(QString.fromUtf8(url))

        # add song
        self.model.appendRow(song)


    def selectFirst(self):
        self.setCurrentIndex(self.model.index(0, 0))


    def autosort(self):
        self.model.autosort()
        self.selectFirst()


    def sort(self):
        self.model.sort(self.horizontalHeader().sortIndicatorSection(),
                        self.horizontalHeader().sortIndicatorOrder())
        self.selectFirst()


    def isEmpty(self):
        if self.model.rowCount == 0:
            return True
        else:
            return False

    def handleEntry(self, path):
        if os.path.isdir(path):
            for entry in self.informer.getDirContent(path):
                url, info, tags = entry
                self.addSong(url, info, tags)
        else:
            url, info, tags = self.informer.getInfo(path)
            self.addSong(url, info, tags)


    def clear(self):
        self.model.removeRows()


    def setColumnHidden_(self, column, hidden):
        self.setColumnHidden(column, hidden)


    def setColumnWidth_(self, column, width):
        self.setColumnWidth(column, width)


    def getState(self):
        return self.horizontalHeader().saveState()


    def setState(self, state):
        self.horizontalHeader().restoreState(state)


    def setSortingColumn(self, column, order=Qt.AscendingOrder):
        self.horizontalHeader().setSortIndicator(column, order)


    def getModel(self):
        return self.model


    def getSongURI(self, num=0):
        index = self.model.index(num, 7)

        return index.data().toString().toUtf8()


    def getCurrentTitle(self):
        index = self.currentIndex().sibling(self.currentIndex().row(), 1)
        return index.data().toString()


    def setCurrentRow(self, row):
        index = self.currentIndex().sibling(row, 0)
        self.setCurrentIndex(index)


    def rowCount(self):
        return self.model.rowCount()


class PlayListModel(QAbstractTableModel):
    def __init__(self, playlist):
        QAbstractTableModel.__init__(self)
        self.playlist = playlist

        #the main storage
        self.table = []


    def index (self, row, column, parent=QModelIndex()):
        return  self.createIndex(row, column)


    def insertRow(self, pos, row):
        self.insertRows(pos, 1, row)


    def insertRows(self, pos, count, rows):
        self.beginInsertRows(QModelIndex(), pos, pos + count - 1)
        self.table.insert(pos, rows)
        self.endInsertRows()
        self.emit(SIGNAL('layoutChanged()'))
        return True


    def removeRow(self, pos):
        self.removeRows(pos, 1)
        return True


    def removeRows(self, row=-1, count=0, parent=QModelIndex()):
        if row == -1:
            self.beginRemoveRows(QModelIndex(), 0, len(self.table) - 1)
            del self.table[:]
        else:
            self.beginRemoveRows(QModelIndex(), row, row + count - 1)
            del self.table[row:row + count]
        self.endRemoveRows()
        self.emit(SIGNAL('layoutChanged()'))
        return True


    def takeRow(self, row):
        rowData = self.table[row]
        self.removeRow(row)
        return rowData


    def appendRow(self, row):
        self.insertRows(len(self.table), 1, row)

    def rowCount(self, parent=QModelIndex()):
        return len(self.table)


    def columnCount(self, parent=QModelIndex()):
        return 8


    def supportedDropActions(self):
        return Qt.MoveAction | Qt.CopyAction


    def dropMimeData(self, data, action, row, column, parent):
        if action == Qt.IgnoreAction:
            return True

        if 'application/x-qabstractitemmodeldatalist' in data.formats():
            import pickle

            rowsPositions = pickle.loads(data.data("application/x-qabstractitemmodeldatalist"))
            theHighest = max(rowsPositions)

            rows = []
            for i in range(len(rowsPositions)):
                curPos = rowsPositions.pop()
                rows.append(self.takeRow(curPos - i))

            rowPos = parent.row() - len(rows)

            while rows:
                if theHighest < parent.row():
                    self.insertRow(rowPos, rows.pop())
                else:
                    self.insertRow(parent.row(), rows.pop())

            return True

        elif 'text/uri-list' in data.formats():
            import os

            for url in data.urls():
                url = str(url.path().toUtf8())
                if self.playlist.informer.hasAudio(url):
                    self.playlist.handleEntry(url)
            return True

        return False


    def mimeTypes(self):
        return ['application/x-qabstractitemmodeldatalist', 'text/uri-list', 'text/plain']


    def mimeData(self, indexes):
        mimeData = QMimeData()

        rows = []

        row = -1
        uris = ''
        files = ''
        for i in range(0, len(indexes)):
            rows.append(indexes[i].row())
            if indexes[i].row() > row:
                path = str(indexes[i].sibling(indexes[i].row(), 7).data().toString().toUtf8())
                uris += path + '\n'
                files += 'file://' + path + '\n'
                row = indexes[i].row()

        import pickle

        mimeData.setData('application/x-qabstractitemmodeldatalist', pickle.dumps(set(rows), 2))
        mimeData.setData('text/uri-list', uris)
        mimeData.setData('text/plain', files)
        return mimeData


    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return QVariant()

        if role != Qt.DisplayRole:
            return QVariant()

        if len(self.table) > index.row():
            return QVariant(self.table[index.row()][index.column()])
        return QVariant()


    def flags(self, index):
        return Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled | Qt.ItemIsEnabled | Qt.ItemIsSelectable

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            if section == 0:
                return QVariant('#')
            if section == 1:
                return QVariant(self.tr('Title'))
            if section == 2:
                return QVariant(self.tr('Artist'))
            if section == 3:
                return  QVariant(self.tr('Album'))
            if section == 4:
                return QVariant(self.tr('Date'))
            if section == 5:
                return QVariant(self.tr('Time'))
            if section == 6:
                return QVariant(self.tr('Genre'))
            if section == 7:
                return QVariant(self.tr('URL'))

        return QVariant()


    def autosort(self):
        self.emit(SIGNAL('layoutAboutToBeChanged()'))
        #self.table = sorted(self.table, key=lambda elem: "%s %s %s" % (elem[2], elem[3], elem[0]))
        self.table.sort(key=lambda elem: "%s %s %s %s" % (elem[2], elem[4], elem[3], elem[0]))
        self.emit(SIGNAL('layoutChanged()'))


    def sort (self, column, order=Qt.AscendingOrder, autosort=False):
        self.emit(SIGNAL('layoutAboutToBeChanged()'))

        reverse = False
        if order == Qt.DescendingOrder:
            reverse = True

        def sk(x):
            val = x[column]
            if column == 0 and '/' in val:
                sp = val.split('/')
                return int(sp[0]) + int(sp[1])
            else:
                return val

        self.table.sort(key=lambda x:sk(x), reverse=reverse)

        self.emit(SIGNAL('layoutChanged()'))


    def match (self, start, role, value, hits=1, flags=Qt.MatchStartsWith):
        for i in range(self.rowCount()):
            if value.toString() == self.table[i][7]:
                return [self.index(i, 0)]
        return None


    def parent(self, index):
        return QModelIndex()

    def update(self):
        self.emit(SIGNAL('layoutChanged()'))
